/*
    Copyright 2007-2011 Patrik Jonsson, sunrise@familjenjonsson.org

    This file is part of Sunrise.

    Sunrise is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Sunrise is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Sunrise.  If not, see <http://www.gnu.org/licenses/>.

*/

/** \file

    Helper functions to create grids of various types, used by the
    mcrx-stage classes. \ingroup mcrx */

#ifndef __create_grid__
#define __create_grid__

#include "boost/shared_ptr.hpp"
#include "CCfits/CCfits"
#include "config.h"
#include "create_grid.h"
#include "grid.h"
#include "grid-fits.h"
#include "full_sed_grid.h"
#include "aux_grid.h"
#include "ir_grid.h"
#include "optical.h"
// include this last to minimize chances of Arepo macros clobbering other stuff
#include "arepo_grid.h"

namespace mcrx {

  /** Helper function to create a full_sed_grid<adaptive_grid>
      object. The extremely ugly syntax with the grid pointer argument
      is so the compiler can resolve which template we are talking
      about, and it has to be a pointer because we can't just create an
      instance of the grid classes. */
  template <typename T_data>
  std::pair<
    boost::shared_ptr<adaptive_grid<T_data> >,
    boost::shared_ptr<full_sed_grid<adaptive_grid<T_data> > > >
  create_grid(CCfits::FITS& input_file, const density_generator& gen, 
	      size_t n_lambda, int decomp_level,
	      full_sed_grid<adaptive_grid<T_data> >*)
  {
    typedef adaptive_grid<T_data> T_grid_impl;
    typedef full_sed_grid<T_grid_impl> T_grid;
    
    CCfits::ExtHDU& structure_hdu = open_HDU(input_file, "GRIDSTRUCTURE");
    CCfits::ExtHDU& data_hdu = open_HDU(input_file, "GRIDDATA");
 
    // first create the adaptive grid
    boost::shared_ptr<T_grid_impl> gg
      (new T_grid_impl(structure_hdu, decomp_level));

    // and then the full_sed_grid
    boost::shared_ptr<T_grid> g
      (new T_grid (gg, structure_hdu, data_hdu, gen, n_lambda));
    
    return make_pair(gg, g);
  }
  

/** Helper function to create an aux_grid<adaptive_grid>
    object. The extremely ugly syntax with the grid pointer argument
    is so the compiler can resolve which template we are talking
    about, and it has to be a pointer because we can't just create an
    instance of the grid classes. */
  template <typename T_data,
	    template<typename> class sampling_policy,
	  typename rng_policy>
  boost::shared_ptr<aux_grid<adaptive_grid<T_data>, sampling_policy, rng_policy> >
  create_grid(CCfits::FITS& input_file, int decomp_level,
	      aux_grid<adaptive_grid<T_data>, sampling_policy, rng_policy>*)
{
  typedef aux_grid<adaptive_grid<T_data>, sampling_policy, rng_policy> T_grid;
  typedef typename T_grid::T_data T_data;
  CCfits::ExtHDU& structure_hdu = open_HDU(input_file, "GRIDSTRUCTURE");
  CCfits::ExtHDU& data_hdu = open_HDU(input_file, "GRIDDATA");

  // first create the adaptive grid
  boost::shared_ptr<adaptive_grid<T_data> > gg
    (new adaptive_grid<T_data> (structure_hdu, decomp_level));
  // and then the aux_grid
  return boost::shared_ptr<T_grid>
    (new T_grid (gg, structure_hdu, data_hdu));
}

/** Helper function to create an ir_grid<adaptive_grid> object, with
    the adaptive_grid object supplied. The extremely ugly syntax with
    the grid pointer argument is so the compiler can resolve which
    template we are talking about, and it has to be a pointer because
    we can't just create an instance of the grid classes. */
  template <typename T_data,
	    template<typename> class sampling_policy,
	    typename rng_policy>
  boost::shared_ptr<ir_grid<adaptive_grid<T_data>, sampling_policy, rng_policy> >
  create_grid(boost::shared_ptr<adaptive_grid<T_data> > gg,
	      CCfits::FITS& input_file, const density_generator& dg, 
	      const array_1& lambda, 
	      ir_grid<adaptive_grid<T_data>, sampling_policy, rng_policy>*)
{
  typedef ir_grid<adaptive_grid<T_data>, sampling_policy, rng_policy> T_grid;
  typedef typename T_grid::T_data T_data;

  CCfits::ExtHDU& structure_hdu = open_HDU(input_file, "GRIDSTRUCTURE");
  CCfits::ExtHDU& data_hdu = open_HDU(input_file, "GRIDDATA");

  // this is easy because the adaptive_grid is supplied
  return boost::shared_ptr<T_grid>
    (new T_grid (gg, structure_hdu, data_hdu, dg, lambda));
}


/** Helper function to create an ir_grid<adaptive_grid> object, without
    the adaptive_grid object supplied. The extremely ugly syntax with
    the grid pointer argument is so the compiler can resolve which
    template we are talking about, and it has to be a pointer because
    we can't just create an instance of the grid classes. */
  template <typename T_data,
	    template<typename> class sampling_policy,
	    typename rng_policy>
  boost::shared_ptr<ir_grid<adaptive_grid<T_data>, sampling_policy, rng_policy> >
  create_grid(CCfits::FITS& input_file, const density_generator& dg, 
	      const array_1& lambda, int decomp_level,
	      ir_grid<adaptive_grid<T_data>, sampling_policy, rng_policy>*)
{
  typedef ir_grid<adaptive_grid<T_data>, sampling_policy, rng_policy> T_grid;
  typedef typename T_grid::T_data T_data;

  CCfits::ExtHDU& structure_hdu = open_HDU(input_file, "GRIDSTRUCTURE");
  CCfits::ExtHDU& data_hdu = open_HDU(input_file, "GRIDDATA");

  // first create the adaptive grid
  boost::shared_ptr<adaptive_grid<T_data> > gg
    (new adaptive_grid<T_data> (structure_hdu, decomp_level));
  // then the ir_grid
  return boost::shared_ptr<T_grid>
    (new T_grid (gg, structure_hdu, data_hdu, dg, lambda));
}


#ifdef WITH_AREPO

/** Helper function to create a full_sed_grid<arepo_grid>
    object. The extremely ugly syntax with the grid pointer argument
    is so the compiler can resolve which template we are talking
    about, and it has to be a pointer because we can't just create an
    instance of the grid classes. */
template <typename T_data>
std::pair<
  boost::shared_ptr<arepo_grid<T_data> >,
  boost::shared_ptr<full_sed_grid<arepo_grid<T_data> > > >
  create_grid(CCfits::FITS& input_file, const density_generator& gen, 
	      int n_lambda, int decomp_level,
	      full_sed_grid<arepo_grid<T_data> >*)
{
  typedef arepo_grid<T_data> T_grid_impl;
  typedef full_sed_grid<T_grid_impl> T_grid;

  boost::shared_ptr<T_grid_impl> gg (new T_grid_impl());

  boost::shared_ptr<T_grid> g(new T_grid (gg, gen, n_lambda));

  return make_pair(gg,g);
}

/** Helper function to create an aux_grid<arepo_grid> object. The
    extremely ugly syntax with the grid pointer argument is so the
    compiler can resolve which template we are talking about, and it
    has to be a pointer because we can't just create an instance of
    the grid classes. */
  template <typename T_data,
	    template<typename> class sampling_policy,
	    typename rng_policy>
  boost::shared_ptr<aux_grid<arepo_grid<T_data>, sampling_policy, rng_policy> >
  create_grid(CCfits::FITS& input_file, int decomp_level,
	      aux_grid<arepo_grid<T_data>, sampling_policy, rng_policy>*)
{
  typedef aux_grid<arepo_grid<T_data>, sampling_policy, rng_policy> T_grid;
  typedef typename T_grid::T_data T_data;

  boost::shared_ptr<arepo_grid<T_data> > gg (new arepo_grid<T_data> ());

  return boost::shared_ptr<T_grid> (new T_grid(gg));
}

/** Helper function to create an ir_sed_grid<arepo_grid> using a
    supplied adaptive_grid. The extremely ugly syntax with the grid
    pointer argument is so the compiler can resolve which template we
    are talking about, and it has to be a pointer because we can't
    just create an instance of the grid classes. */
  template <typename T_data, 
	    template<typename> class sampling_policy,
	    typename rng_policy>
  boost::shared_ptr<ir_grid<arepo_grid<T_data>, sampling_policy, rng_policy> >
  create_grid(boost::shared_ptr<arepo_grid<T_data> > gg,
	      CCfits::FITS& input_file, 
	      const density_generator& dg, const array_1& lambda,
	      ir_grid<arepo_grid<T_data>, sampling_policy, rng_policy>*)
{
  typedef ir_grid<arepo_grid<T_data>, sampling_policy, rng_policy> T_grid;
  typedef typename T_grid::T_data T_data;

  return boost::shared_ptr<T_grid> (new T_grid(gg, dg, lambda));
}


/** Helper function to create an ir_sed_grid<arepo_grid> using a
    supplied adaptive_grid. The extremely ugly syntax with the grid
    pointer argument is so the compiler can resolve which template we
    are talking about, and it has to be a pointer because we can't
    just create an instance of the grid classes. */
  template <typename T_data, 
	    template<typename> class sampling_policy,
	    typename rng_policy>
  boost::shared_ptr<ir_grid<arepo_grid<T_data>, sampling_policy, rng_policy> >
  create_grid(CCfits::FITS& input_file, 
	      const density_generator& dg, const array_1& lambda,
	      int decomp_level,
	      ir_grid<arepo_grid<T_data>, sampling_policy, rng_policy>*)
{
  typedef ir_grid<arepo_grid<T_data>, sampling_policy, rng_policy> T_grid;
  typedef typename T_grid::T_data T_data;

  boost::shared_ptr<arepo_grid<T_data> > gg (new arepo_grid<T_data> ());

  return boost::shared_ptr<T_grid> (new T_grid(gg, dg, lambda));
}


#endif
}

#endif
